/*  
 * @(#)Main.java              Project:Test  
 * Date:2013-1-9  
 *  
 * Copyright (c) 2013 Geek_Soledad.  
 * All rights reserved.  
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");  
 *  you may not use this file except in compliance with the License.  
 * You may obtain a copy of the License at  
 *  
 *     http://www.apache.org/licenses/LICENSE-2.0  
 *  
 * Unless required by applicable law or agreed to in writing, software  
 * distributed under the License is distributed on an "AS IS" BASIS,  
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
 * See the License for the specific language governing permissions and  
 * limitations under the License.  
 */
package com.sinaapp.msdxblog.androidsecurity;

import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * @author Geek_Soledad (msdx.android@tom.com)
 */
public class CrackPswd {
	public static final int[] indexs = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };

	private String text = "66 b2 81 69 69 2e 0d fc 96 ae 07 ab 5a 9b 71 5e dc f7 da 68";
	private byte[] pswd;
	private int sum = 36;
	private MessageDigest md;

	public CrackPswd() {
		try {
			md = MessageDigest.getInstance("SHA1");
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		text = trim(text);
	}

	public CrackPswd(String source) {
		text = source;
		try {
			md = MessageDigest.getInstance("SHA1");
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		text = trim(text);
	}

	/**
	 * 去除空格。
	 * 
	 * @param data
	 * @return
	 */
	public String trim(final String data) {
		return text.replaceAll(" ", "");
	}

	public static void main(String[] args) {
		try {
			long time = System.nanoTime();
			byte[] bt = HexFinder.readFile("d:\\gesture.key");
			String hexData = HexFinder.format(bt);
			CrackPswd crackPswd = new CrackPswd("");
			System.out.println(crackPswd.tryToFind());
			System.out.println("耗费时间为：" + (System.nanoTime() - time)
					/ 1000000000.0 + "秒");
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	/**
	 * 计算SHA值并比较结果。
	 * 
	 * @param pswd
	 * @return 当匹配时返回true，否则返回false。
	 */
	private final boolean calcAndCompared(final byte[] pswd) {
		byte[] result = md.digest(pswd);
		return text.equals(StringUtil.bytesToHexString(result));
	}

	/**
	 * 尝试暴力破解。
	 * 
	 * @return
	 */
	public final String tryToFind() {
		long time = System.nanoTime();
		for (int i = 4; i <= 7; i++) {
			pswd = new byte[i];
			for (int j = 0; j < pswd.length; j++) {
				pswd[j] = (byte) j;
			}
			pswd[pswd.length - 1]--;
			int count = 0;
			int index = nextPswd(pswd, i - 1);
			while (index > -1) {
				if (check(pswd)) {
					if (calcAndCompared(pswd)) {
						return parseToString(pswd);
					}
					count++;
				}
				index = nextPswd(pswd, i - 1);
			}
			System.out.println(i + "位数的解锁密码个数为" + count);
		}
		byte pswd9[] = new byte[9];
		pswd = new byte[8];
		int index = nextPswd(pswd, 7);
		int count = 0;
		while (index > -1) {
			if (check(pswd)) {
				if (calcAndCompared(pswd)) {
					return parseToString(pswd);
				}
				calcAndCompared(pswd);
				count++;
				for (int i = 0; i < 8; i++) {
					pswd9[i] = pswd[i];
				}
				pswd9[8] = (byte) findTheNineth(pswd);
				if (calcAndCompared(pswd9)) {
					return parseToString(pswd9);
				}
			}
			index = nextPswd(pswd, 7);
		}
		System.out.println("8位数及9位数的解锁密码个数均为" + count);
		System.out.println(System.nanoTime() - time);
		return null;
	}

	private int findTheNineth(final byte[] pswd) {
		sum = 36;
		for (int i = 0; i < 8; i++) {
			sum -= pswd[i];
		}
		return sum;
	}

	/**
	 * 将字节数组转为字符串。
	 * 
	 * @param data
	 * @return
	 */
	private final String parseToString(final byte[] data) {
		StringBuilder sb = new StringBuilder();
		for (byte p : data) {
			sb.append(p);
		}
		return sb.toString();
	}

	/**
	 * 模拟9进制，生成下一个数组。
	 * 
	 * @param pswd
	 * @param index
	 *            进行累加的元素下标。
	 * @return
	 */
	public final int nextPswd(final byte[] pswd, int index) {
		if (index != -1) {
			pswd[index] = nextValueByIndex(pswd, index, pswd[index]);
			if (pswd[index] == 9) {
				int i = nextPswd(pswd, index - 1);
				pswd[index] = nextValueByIndex(pswd, index, -1);
				return i;
			} else {
				return index;
			}
		}
		return -1;
	}

	/**
	 * 当前下标的下一个可能的值
	 * 
	 * @param pswd
	 *            生成的密码
	 * @param index
	 *            当前递增的位的下标。
	 * @param value
	 *            下一个合法的值。它必须不能与前面出现的数字重复。
	 * @return
	 */
	public final byte nextValueByIndex(final byte[] pswd, int index, int value) {
		value++;
		while (value < 9) {
			if (isContain(pswd, index, value)) {
				value++;
			} else {
				return (byte) value;
			}
		}
		return (byte) value;
	}

	/**
	 * 当前递增的位的数字是否已经与前面的重复。
	 * 
	 * @param pswd
	 *            生成的密码
	 * @param index
	 *            当前递增的位的下标。
	 * @param value
	 *            当前递增的值。
	 * @return
	 */
	public final boolean isContain(final byte[] pswd, int index, int value) {
		for (int i = 0; i < index; i++) {
			if (pswd[i] == value) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 检查是否合法
	 * 
	 * @param pswd
	 * @return
	 */
	public final boolean check(final byte[] pswd) {
		int a, c;
		for (int i = 0, length = pswd.length - 1; i < length; i++) {
			a = pswd[i];
			for (int j = i + 1, l = pswd.length; j < l; j++) {
				if (a == pswd[j]) {
					return false;
				}
			}
			c = a + pswd[i + 1];
			if (c == 8) {
				if (isNotContain(pswd, i, 4)) { // 去除相邻两位为08,17,26,35,53,62,71,80时，前面没有4的情况。
					return false;
				}
			} else if (c == 2) {
				if (isNotContain(pswd, i, 1)) {// 去除相邻两位为02,20时前面没有1的情况。
					return false;
				}
			} else if (c == 14) {
				if (isNotContain(pswd, i, 7)) {// 去除相邻两位为68,86时前面没有7的情况。
					return false;
				}
			} else if (c == 6) {
				if ((a == 0 || a == 6) && isNotContain(pswd, i, 3)) {
					return false;
				}
			} else if (c == 10) {
				if ((a == 2 || a == 8) && isNotContain(pswd, i, 5)) {
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * 在第i个元素前不包含数字v。
	 * 
	 * @param pswd
	 * @param index
	 * @param value
	 * @return
	 */
	public final boolean isNotContain(final byte[] pswd, final int index,
			final int value) {
		for (int i = 0; i < index; i++) {
			if (pswd[i] == value) {
				return false;
			}
		}
		return true;
	}
}
